﻿#include <thread/thread.h>

#include "util/util.h"

#ifdef CPPC_OS_WINDOWS
#include <Windows.h>
#include <stdlib.h>
#else
#include <fcntl.h>
#include <pthread.h>
#include <semaphore.h>
#include <sys/prctl.h>
#include <sys/select.h>
#include <sys/types.h>
#include <unistd.h>
#endif

#include "thread.h"
#include <cassert>
#include <future>
#include <iostream>
#include <signal.h>
#include <sstream>
#include <string.h>
#include <string>
#include <thread>
#include <time.h>

namespace cppc {
Thread::Thread() :
    m_state(ThreadState::Stopped) {
}

Thread::~Thread() {
    stop();
    join();
}

std::thread::id Thread::id() const {
    std::lock_guard<std::mutex> lock(m_mutex);
    return m_thread.get_id();
}

std::string Thread::threadName() const {
    return m_threadName;
}

void Thread::setThreadName(const std::string &threadName) {
    m_threadName = threadName;
}

Thread::ThreadState Thread::state() {
    std::lock_guard<std::mutex> lock(m_mutex);
    return m_state;
}
void Thread::start() {
    std::unique_lock<std::mutex> lock(m_mutex);
    if (m_state == ThreadState::Started || m_state == ThreadState::Starting) {
        return;
    }

    if (m_state == ThreadState::Stopping) {
        m_cond.wait(lock, [this] { return m_state == ThreadState::Stopped; });
    }

    m_state = ThreadState::Starting;
    m_cond.notify_all();

    m_thread = std::thread([this] { exec(); });
    m_cond.wait(lock, [this] { return m_state == ThreadState::Started || m_state == ThreadState::Stopped;; });
}

void Thread::stop() {
    std::unique_lock<std::mutex> lock(m_mutex);
    if (m_state == ThreadState::Stopping || m_state == ThreadState::Stopped) {
        return;
    }

    if (m_state == ThreadState::Starting) {
        m_cond.wait(lock, [this] { return m_state == ThreadState::Started; });
    }

    m_state = ThreadState::Stopping;
    m_cond.notify_all();
}

void Thread::join() {
    std::unique_lock<std::mutex> lock(m_mutex);
    if (m_state != ThreadState::Stopped) {
        m_cond.wait(lock, [this] { return m_state == ThreadState::Stopped; });
    }

    if (m_thread.joinable()) {
        m_thread.join();
    }
}

void Thread::kill() {
#ifdef CPPC_OS_WINDOWS
    // Windows 平台
    TerminateThread(m_thread.native_handle(), 0);
#else
    // Linux 平台
    pthread_cancel(m_thread.native_handle());
#endif
}

bool Thread::isRunning() {
    std::lock_guard<std::mutex> lock(m_mutex);
    return m_state == ThreadState::Starting || m_state == ThreadState::Started;
}
void Thread::exec() {
    {
        m_mutex.lock();
        if (m_state != ThreadState::Starting) {
            m_mutex.unlock();
            return;
        }
        setCurrentThreadName(m_threadName);

        willRun(); // 由具体子类实现
        m_state = ThreadState::Started;
        m_cond.notify_all();
        m_mutex.unlock();
    }

    run(); // 由具体子类实现
    {
        m_mutex.lock();
        willStop(); // 由具体子类实现
        m_state = ThreadState::Stopped;
        m_cond.notify_all();
        m_mutex.unlock();
    }
}

void Thread::run() {
}
void Thread::willRun() {
}
void Thread::willStop() {
}

unsigned long long Thread::currentProcessId() {
#ifdef CPPC_OS_WINDOWS
    return GetCurrentProcessId();
#else
    return getpid();
#endif
}

unsigned long Thread::currentThreadId() {
#ifdef CPPC_OS_WINDOWS
    return GetCurrentThreadId();
#else
    pthread_t threadId = pthread_self();
    return static_cast<unsigned long>(reinterpret_cast<uintptr_t>(threadId));
#endif
}

int Thread::sleepMS(long milliseconds) {
#ifdef CPPC_OS_WINDOWS
    Sleep(milliseconds);
    return 0;
#else
    return usleep(milliseconds * 1000);
#endif
}

int Thread::sleep(long seconds) {
#ifdef CPPC_OS_WINDOWS
    Sleep(seconds * 1000);
    return 0;
#else
    return usleep(seconds * 1000000);
#endif
}

std::string Thread::currentThreadName() {
#if ((defined(__linux) || defined(__linux__)) && !defined(ANDROID)) || (defined(__MACH__) || defined(__APPLE__)) || (defined(ANDROID) && __ANDROID_API__ >= 26) || defined(__MINGW32__)
    std::string ret;
    ret.resize(32);
    const char *retChars = ret.c_str();
    prctl(PR_GET_NAME, retChars);
    return ret;
#elif defined(_MSC_VER)
    using GetThreadDescriptionFunc = HRESULT(WINAPI *)(_In_ HANDLE hThread, _In_ PWSTR * ppszThreadDescription);
    static auto getThreadDescription = reinterpret_cast<GetThreadDescriptionFunc>(::GetProcAddress(::GetModuleHandleA("Kernel32.dll"), "GetThreadDescription"));

    if (!getThreadDescription) {
        std::ostringstream ss;
        ss << std::this_thread::get_id();
        return ss.str();
    } else {
        PWSTR data;
        HRESULT hr = getThreadDescription(GetCurrentThread(), &data);
        if (SUCCEEDED(hr) && data[0] != '\0') {
            char threadName[MAX_PATH];
            size_t numCharsConverted;
            errno_t charResult = wcstombs_s(&numCharsConverted, threadName, data, MAX_PATH - 1);
            if (charResult == 0) {
                LocalFree(data);
                std::ostringstream ss;
                ss << threadName;
                return ss.str();
            } else {
                if (data) {
                    LocalFree(data);
                }
                return std::to_string((uint64_t)GetCurrentThreadId());
            }
        } else {
            if (data) {
                LocalFree(data);
            }
            return std::to_string((uint64_t)GetCurrentThreadId());
        }
    }
#else
    std::ostringstream ss;
    ss << std::this_thread::get_id();
    return ss.str();
#endif
}

void Thread::setCurrentThreadName(const std::string &threadName) {
    if (!threadName.empty()) {
#if defined(__linux) || defined(__linux__) || defined(__MINGW32__)
        prctl(PR_SET_NAME, threadName.c_str());
        // pthread_setname_np(pthread_self(), cppc::limitStringSize(threadName, 16).data());
        // prctl(PR_SET_NAME, threadName.c_str(), NULL, NULL, NULL);
#elif defined(__MACH__) || defined(__APPLE__)
        pthread_setname_np(limitStringSize(threadName, 32).data());
#elif defined(_MSC_VER)
        using SetThreadDescriptionFunc = HRESULT(WINAPI *)(_In_ HANDLE hThread, _In_ PCWSTR lpThreadDescription);
        static auto setThreadDescription = reinterpret_cast<SetThreadDescriptionFunc>(
            ::GetProcAddress(::GetModuleHandleA("Kernel32.dll"), "SetThreadDescription"));

        if (setThreadDescription) {
            // Convert the thread name to Unicode
            wchar_t threadNameW[MAX_PATH];
            size_t numCharsConverted;
            errno_t wcharResult = mbstowcs_s(&numCharsConverted, threadNameW, MAX_PATH - 1, threadName.c_str(), MAX_PATH - 1);
            if (wcharResult == 0) {
                HRESULT hr = setThreadDescription(::GetCurrentThread(), threadNameW);
                if (!SUCCEEDED(hr)) {
                    std::cerr << "Failed to set thread description: " << hr << std::endl;
                }
            }
        } else {
            const DWORD MS_VC_EXCEPTION = 0x406D1388;
#pragma pack(push, 8)
            struct THREADNAME_INFO {
                DWORD dwType = 0x1000; // Must be 0x1000
                LPCSTR szName;         // Pointer to name (in user address space)
                DWORD dwThreadID;      // Thread ID (-1 for caller thread)
                DWORD dwFlags = 0;     // Reserved for future use; must be zero
            };
#pragma pack(pop)

            THREADNAME_INFO info;
            info.szName = threadName.c_str();
            info.dwThreadID = (DWORD)-1;

            __try {
                RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), reinterpret_cast<const ULONG_PTR *>(&info));
            } __except (GetExceptionCode() == MS_VC_EXCEPTION ? EXCEPTION_CONTINUE_EXECUTION : EXCEPTION_EXECUTE_HANDLER) {
            }
        }
#endif
    }
}

} // namespace cppc
